package com.back.service.impl;

import com.back.exception.BusinessException;
import com.back.exception.ErrorCode;
import com.back.exception.ThrowUtils;
import com.back.model.dto.hero.HeroAddRequest;
import com.back.model.dto.hero.HeroQueryRequest;
import com.back.model.dto.hero.HeroUpdateRequest;
import com.back.model.entity.Hero;
import com.back.model.vo.hero.HeroVO;
import com.back.service.HeroService;
import com.back.mapper.HeroMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wangdan
 * @description 针对表【Hero】的数据库操作Service实现
 * @createDate 2025-09-12 09:23:53
 */
@Service // 让Spring识别这是服务层实现类
public class HeroServiceImpl extends ServiceImpl<HeroMapper, Hero>
        implements HeroService{

    @Resource
    private HeroMapper heroMapper;

    @Override
    public long addHero(HeroAddRequest heroAddRequest) {
        ThrowUtils.throwIf(heroAddRequest == null, ErrorCode.PARAMS_ERROR);

        Hero hero = new Hero();
        BeanUtils.copyProperties(heroAddRequest, hero);

        // 校验英雄信息
        validHero(hero, true);

        // 保存到数据库
        boolean result = this.save(hero);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);

        return hero.getId();
    }

    @Override
    public boolean deleteHero(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);

        // 判断是否存在
        Hero oldHero = this.getById(id);
        ThrowUtils.throwIf(oldHero == null, ErrorCode.NOT_FOUND_ERROR);

        // 删除
        return this.removeById(id);
    }

    @Override
    @Transactional
    public boolean updateHero(HeroUpdateRequest heroUpdateRequest) {
        ThrowUtils.throwIf(heroUpdateRequest == null || heroUpdateRequest.getId() == null, ErrorCode.PARAMS_ERROR);

        Hero hero = new Hero();
        BeanUtils.copyProperties(heroUpdateRequest, hero);

        // 校验英雄信息
        validHero(hero, false);

        // 判断是否存在
        long id = heroUpdateRequest.getId();
        Hero oldHero = this.getById(id);
        ThrowUtils.throwIf(oldHero == null, ErrorCode.NOT_FOUND_ERROR);

        // 更新
        return this.updateById(hero);
    }


    @Override // 表示重写 HeroService 接口中定义的 getHeroVOById 方法
    public HeroVO getHeroVOById(long id) {
        // 1. 第一步：参数二次校验（防止无效ID传入）
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 逻辑：如果传入的ID是负数或0（无效ID），就抛出“参数错误”异常
        // 为什么二次校验？虽然Controller层也校验了，但服务层再校验是“防御性编程”——避免其他地方直接调用服务层时跳过校验

        // 2. 第二步：从数据库查询英雄原始数据（Hero是数据库表对应的实体类）
        Hero hero = this.getById(id);
        // 解释：
        // - “this” 指当前 HeroServiceImpl 实例；
        // - “getById(id)” 是 MyBatis-Plus 框架提供的“根据主键查数据”的通用方法（前提是 HeroServiceImpl 继承了 MyBatis-Plus 的 ServiceImpl 类）；
        // - 作用：根据传入的 id，去数据库的“英雄表”中查询对应的一行数据，并封装成 Hero 实体对象（Hero 的字段和数据库表字段一一对应）。

        // 3. 第三步：校验查询结果（防止查不到数据）
        ThrowUtils.throwIf(hero == null, ErrorCode.NOT_FOUND_ERROR);
        // 逻辑：如果数据库中没有找到对应 id 的英雄（hero 为 null），就抛出“资源不存在”异常
        // 比如前端传了一个不存在的 id=999，这里就会报错，告诉前端“查不到这个英雄”

        // 4. 第四步：将数据库实体（Hero）转换为前端需要的视图对象（HeroVO），并返回
        return getHeroVO(hero);
        // 关键：为什么要转 VO？
        // - Hero 是数据库实体，包含表中所有字段（可能有前端不需要的敏感字段，如创建时间、数据库主键规则等）；
        // - HeroVO 是“视图对象”，只包含前端需要展示的字段（如英雄名称、属性、背景故事等），是专门给前端“定制”的数据格式；
        // - “getHeroVO(hero)” 是自定义方法（你没贴，逻辑就是把 Hero 的字段赋值给 HeroVO，比如 hero.getName() 赋值给 heroVO.setName()）。
    }

    @Override
    public IPage<HeroVO> listHeroVOByPage(HeroQueryRequest heroQueryRequest) {
        long current = heroQueryRequest.getCurrent();
        long size = heroQueryRequest.getPageSize();

        // 创建分页对象
        Page<Hero> heroPage = new Page<>(current, size);

        // 查询数据
        IPage<Hero> heroIPage = heroMapper.listHeroByPage(heroPage,
                heroQueryRequest.getHeroName(),
                heroQueryRequest.getTitle(),
                heroQueryRequest.getWeaponType(),
                heroQueryRequest.getAttributeType(),
                heroQueryRequest.getStarRingDivision(),
                heroQueryRequest.getDamageType());

        // 转换为VO
        IPage<HeroVO> heroVOIPage = new Page<>(current, size, heroIPage.getTotal());
        List<HeroVO> heroVOList = heroIPage.getRecords().stream()
                .map(this::getHeroVO)
                .collect(Collectors.toList());
        heroVOIPage.setRecords(heroVOList);

        return heroVOIPage;
    }

    @Override
    public List<HeroVO> getAllHeroVOList() {
        List<Hero> heroList = heroMapper.getAllHeroes();
        return heroList.stream()
                .map(this::getHeroVO)
                .collect(Collectors.toList());
    }

    @Override
    public void validHero(Hero hero, boolean add) {
        ThrowUtils.throwIf(hero == null, ErrorCode.PARAMS_ERROR);

        String heroName = hero.getHeroName();
        String title = hero.getTitle();
        String weaponType = hero.getWeaponType();
        String attributeType = hero.getAttributeType();
        String starRingDivision = hero.getStarRingDivision();
        String damageType = hero.getDamageType();
        String backgroundDescription = hero.getBackgroundDescription();
        String smallIconUrl = hero.getSmallIconUrl();
        String largeIconUrl = hero.getLargeIconUrl();
        String videoUrl = hero.getVideoUrl();

        // 创建时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(StringUtils.isBlank(heroName), ErrorCode.PARAMS_ERROR, "英雄名称不能为空");
        }

        // 有参数则校验
        if (StringUtils.isNotBlank(heroName) && heroName.length() > 100) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "英雄名称过长");
        }
        if (StringUtils.isNotBlank(title) && title.length() > 100) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "称谓过长");
        }
        if (StringUtils.isNotBlank(weaponType) && weaponType.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "武器类型过长");
        }
        if (StringUtils.isNotBlank(attributeType) && attributeType.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "属性类型过长");
        }
        if (StringUtils.isNotBlank(starRingDivision) && starRingDivision.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "星之环分野过长");
        }
        if (StringUtils.isNotBlank(damageType) && damageType.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "伤害类型过长");
        }
        if (StringUtils.isNotBlank(smallIconUrl) && smallIconUrl.length() > 255) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "小图标URL过长");
        }
        if (StringUtils.isNotBlank(largeIconUrl) && largeIconUrl.length() > 255) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "大图标URL过长");
        }
        if (StringUtils.isNotBlank(videoUrl) && videoUrl.length() > 255) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "视频URL过长");
        }
    }

    @Override
    public HeroVO getHeroVO(Hero hero) {
        if (hero == null) {
            return null;
        }

        HeroVO heroVO = new HeroVO();
        BeanUtils.copyProperties(hero, heroVO);

        return heroVO;
    }

}
